#include"util.hpp"
#include"threadPool.hpp"
#include"task.hpp"
#include"daemonize.hpp"
#include "protocol.hpp"

//这里是确保不会出现除零和模零的情况
//初始化列表中包含了5个元素，每个元素都是一个键值对，用于初始化opFunctions这个std::map。
std::map<char,std::function<int(int,int)>>opFunctions
{
    {'+',[](int elemOne, int elemTwo){return elemOne+elemTwo;}},
    {'-',[](int elemOne, int elemTwo){return elemOne-elemTwo;}},
    {'*',[](int elemOne, int elemTwo){return elemOne*elemTwo;}},
    {'/',[](int elemOne, int elemTwo){return elemOne/elemTwo;}},
    {'%',[](int elemOne, int elemTwo){return elemOne%elemTwo;}}
};
static response calculator(const request& req)
{
    response resp;
    int x=req.get_x();
    int y=req.get_y();
    int op=req.get_op();

    if(opFunctions.find(req.get_op())==opFunctions.end())
    {
        resp.set_exitCode(-3);
    }
    else
    {
        if(y==0&&op=='/'){
            resp.set_exitCode(-1);
        }
        else if(y==0&&op=='%'){
            resp.set_exitCode(-2);
        }
        else{
            resp.set_result(opFunctions[op](x,y));
        }
    }
    return resp;
}
//任务处理的具体逻辑，将它作为参数传给Task对象
//该函数 netCal 主要用于处理网络通信中的客户端请求，包括读取数据、解析请求、计算结果以及发送响应。
void netCal(int sock,const std::string& clientIp,uint16_t clientPort)
{
    assert(sock>=0);
    assert(!clientIp.empty());
    assert(clientPort>=1024);
    std::string inBuffer;
    while(true)
    {
        request req;
        char buffer[128];
        ssize_t s=read(sock,buffer,sizeof(buffer)-1);
        if(s==0)
        {
            logMessage(NOTICE,"client[%s:%d] close socket,service done...",clientIp.c_str(),clientPort);
            break;
        }
        else if(s<0){
            logMessage(WARINING,"read client[%s:%d] error,errorCode :%d,errorMessage :%s",clientIp.c_str(),clientPort,errno,strerror(errno));
        }
        // 走到这里 读取成功
        // 但是, 读取到的内容是什么呢?
        // 本次读取, 有没有可能读取到的只是发送过来的一部分呢? 如果发送了一条或者多条完整strPackage, 却没有读取完整呢?
        // 这种情况是有可能发生的, 所以不能直接进行 decode 以及 反序列化, 需要先检查
        buffer[s]='\0';
        inBuffer+=buffer;
        uint32_t strPackageLen=0;
        std::string package=decode(inBuffer,&strPackageLen);
        if(strPackageLen==0)
        continue;
        if(req.deserialize(package))
        {
            response resp=calculator(req);
            std::string respPackage;
            resp.serialize(&respPackage);
            respPackage=encode(respPackage,respPackage.size());
            write(sock,respPackage.c_str(),respPackage.size());
        }
    }
}

class tcpServer
{
    public:
        tcpServer(uint16_t port,const std::string& ip="")
            :_port(port)
            ,_ip(ip)
            ,_LiSockFd(-1){}
        void Init()
        {
            _LiSockFd=socket(AF_INET,SOCK_STREAM,0);
            if(_LiSockFd<0)
            {
                logMessage(FATAL,"socket() failed::%s : %d",strerror(errno),_LiSockFd);
                exit(SOCKET_ERR);
            }
            logMessage(DEBUG,"socket() success::%d",_LiSockFd);
            struct sockaddr_in local;
            std::memset(&local,0,sizeof(local));
            local.sin_family=AF_INET;
            local.sin_port=htons(_port);
            _ip.empty()?(local.sin_addr.s_addr=htonl(INADDR_ANY)):(inet_aton(_ip.c_str(),&local.sin_addr));
            
            if(bind(_LiSockFd,(const struct sockaddr*)&local,sizeof(local))==-1)
            {
                logMessage(FATAL,"socket() failed::%s : %d",strerror(errno),_LiSockFd);
                exit(BIND_ERR);
            }
            logMessage(DEBUG,"bind() success::%d",_LiSockFd);

            if(listen(_LiSockFd,5)==-1)
            {
                logMessage(FATAL,"listen() failed::%s : %d",strerror(errno),_LiSockFd);
                exit(LISTEN_ERR);
            }
            logMessage(DEBUG,"listen() success::%d",_LiSockFd);

            _tp->threadPool<Task>::getInstance();
        }

        void loop()
        {
            _tp->start();
            logMessage(DEBUG,"threadPool start,thread num:%d",_tp->getThreadNum());
            while(true)
            {
                struct sockaddr_in peer;
                socklen_t peerlen=sizeof(peer);
                
                int serviceSockFd=accept(_LiSockFd,(struct sockaddr*)&peer,&peerlen);
                if(serviceSockFd==-1)
                {
                    logMessage(WARINING,"accept() failed::%s : %d",strerror(errno),_LiSockFd);
                    continue;
                }
                std::string peerIp=inet_ntoa(peer.sin_addr);
                uint16_t peerPort=ntohs(peer.sin_port);
                logMessage(DEBUG,"accept() success:: [%s: %d] | %d",peerIp.c_str(),peerPort,serviceSockFd);
                Task t(serviceSockFd,peerIp,peerPort,netCal);
                _tp->pushTask(t);
            }
        }
    private:
        uint16_t _port;
        std::string _ip;
        int _LiSockFd;
        threadPool<Task>* _tp;
};

void Usage(string proc)
{
    std::cerr<<"Usage::\n\t"<<proc<<"port ip"<<std::endl;
    std::cerr<<"example::\n\t"<<proc<<"8080 127.0.0.1"<<std::endl;
}

int main(int argc,char* argv[])
{
    if(argc!=3&&argc!=2)
    {
        Usage(argv[0]);
        exit(USE_ERR);
    }
    uint16_t port=atoi(argv[1]);
    string ip;
    if(argc==3)
    {
        ip=argv[2];
    }
    daemonize();
    log log;
    log.enable();
    tcpServer Usvr(port,ip);
    Usvr.Init();
    Usvr.loop();
    return 0;
}